package videobuy

import (
	"context"
	"fmt"
	"gitee.com/bobo-rs/creative-framework/pkg/algorithm/uniquecode"
	"gitee.com/bobo-rs/creative-framework/pkg/utils"
	"gitee.com/bobo-rs/innovideo-services/consts"
	"gitee.com/bobo-rs/innovideo-services/enums"
	"gitee.com/bobo-rs/innovideo-services/framework/dao"
	"gitee.com/bobo-rs/innovideo-services/framework/model"
	"gitee.com/bobo-rs/innovideo-services/framework/model/entity"
	"gitee.com/bobo-rs/innovideo-services/framework/service"
	"gitee.com/bobo-rs/innovideo-services/library/exception"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"strings"
)

// CreateVideoPurchase 创建用户购买视频数据
func (b *sVideoBuy) CreateVideoPurchase(ctx context.Context, in model.CreateVideoPurchaseInput) error {
	// 处理并生成销售账单数据
	item, err := b.PrepareVideoSaleBill(ctx, &in)
	if err != nil {
		return err
	}

	// 生成视频销售账单
	return b.GenVideoSaleBillWithTX(ctx, *item)
}

// GenVideoSaleBillWithTX 生成视频购买销售账单-事务处理
func (b *sVideoBuy) GenVideoSaleBillWithTX(ctx context.Context, in model.GenVideoSaleBillItem) error {
	// 事物处理
	return dao.VideoBuy.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		return b.GenVideoSaleBill(ctx, in)
	})
}

// GenVideoSaleBill 生成视频购买销售账单
func (b *sVideoBuy) GenVideoSaleBill(ctx context.Context, in model.GenVideoSaleBillItem) error {
	// 获取用户资金信息
	_, err := service.User().GetAndValidateFundsCoinsByUid(ctx, in.Uid, in.ConsumedCoins)
	if err != nil {
		return err
	}

	// 生成并获取购买ID
	buyId, err := b.GenAndGetBuyId(ctx, entity.VideoBuy{
		VideoId:           in.AuthDetail.Id,
		Uid:               in.Uid,
		VideoName:         in.AuthDetail.VideoName,
		CompletePanguCoin: in.AuthDetail.CompletePanguCoin,
		AddPanguCoin:      in.ConsumedCoins,
		Status:            uint(in.BuyStatus),
	})
	if err != nil {
		return err
	}

	// 生成账单
	billId, err := b.GenBuyBillAndGetId(ctx, entity.VideoBuyBill{
		BuyId:          buyId,
		Uid:            in.Uid,
		BuyPanguCoin:   in.ConsumedCoins,
		BuyEpisodesNum: uint(len(in.Episodes)),
		Content: fmt.Sprintf(
			`%s:%s`,
			in.AuthDetail.VideoName,
			strings.Join(utils.NewArray(in.Episodes).Strings(dao.VideoEpisodes.Columns().EpisodesName), `,`),
		),
		Type: uint(in.BuyType),
	})
	if err != nil {
		return err
	}

	// 生成账单剧集列表
	err = b.GenBuyEpisodes(ctx, model.GenBuyEpisodesInput{
		BuyId:    buyId,
		BillId:   billId,
		Uid:      in.Uid,
		VideoId:  in.AuthDetail.Id,
		Episodes: in.Episodes,
	})
	if err != nil {
		return err
	}

	// 更新账户资金
	return service.PgCoin().GenPGCoinConsumeBill(ctx, model.PGCoinConsumeInput{
		ConsumePGCoin: in.ConsumedCoins,
		Uid:           in.Uid,
	})
}

// GetEpisodesBuyList 获取剧集购买列表
func (b *sVideoBuy) GetEpisodesBuyList(ctx context.Context, uid uint, in model.CreateVideoPurchaseInput) (episodes []model.VideoEpisodesBuyItem, err error) {
	// 余集购买
	if in.BuyType == enums.VideoBuyTypeME {
		moreEpisodesId, _ := b.GetBuyEpisodesIdSet(ctx, in.VideoId, uid)
		return service.Video().GetRemainderNotBuyList(ctx, moreEpisodesId)

	}
	// 单集或全集
	return service.Video().GetEpisodesBuyList(
		ctx, in.VideoId, in.EpisodesIdSet,
	)
}

// GenAndGetBuyId 生成并获取购买ID
func (b *sVideoBuy) GenAndGetBuyId(ctx context.Context, data entity.VideoBuy) (uint, error) {
	// 获取购买ID
	detail := model.VideoBuyItem{}
	_ = b.BuyModel().Scan(ctx, g.Map{
		dao.VideoBuy.Columns().VideoId: data.VideoId,
		dao.VideoBuy.Columns().Uid:     data.Uid,
	}, &detail)
	if detail.Id > 0 {
		// 是否购买完成
		if detail.Status == uint(enums.VideoBuyStatusYes) {
			return 0, exception.New(`已购买全集`)
		}
		// 返回购买并更新数据
		return detail.Id, b.UpdateVideoBuyById(ctx, data.Id, g.Map{
			dao.VideoBuy.Columns().AddPanguCoin: fmt.Sprintf(`%s + %d`, dao.VideoBuy.Columns().AddPanguCoin, data.AddPanguCoin),
			dao.VideoBuy.Columns().Status:       data.Status,
		})
	}

	data.BuyNo = uniquecode.UniSerialCode(ctx, consts.VideoBuyNoPrefix, true)
	// 添加购买视频并获取购买ID
	buyId, err := dao.VideoBuy.Ctx(ctx).InsertAndGetId(data)
	if err != nil {
		return 0, exception.Newf(`创建购买订单失败 %s`, err.Error())
	}
	return uint(buyId), nil
}

// UpdateVideoBuyById 通过购买ID更新视频购买信息
func (b *sVideoBuy) UpdateVideoBuyById(ctx context.Context, buyId uint, data interface{}) error {
	// 更新数据
	return b.BuyModel().Update(ctx, g.Map{
		dao.VideoBuy.Columns().Id: buyId,
	}, data)
}

// PrepareVideoSaleBill 准备销售账单的数据
func (b *sVideoBuy) PrepareVideoSaleBill(ctx context.Context, in *model.CreateVideoPurchaseInput) (item *model.GenVideoSaleBillItem, err error) {
	// 获取视频详情属性
	detail, err := service.Video().GetVideoAuthDetail(ctx, in.VideoId)
	if err != nil {
		return nil, err
	}
	// 是否免费
	if detail.FeeType == int(enums.VideoFeeTypeFee) {
		return nil, exception.New(`免费视频无需购买`)
	}

	// 获取用户ID
	uid := service.BizCtx().GetUid(ctx)

	// 全集购买-验证之前是否已购买过视频，若已购买过视频，则默认购买余下全部剧集，更改购买方式
	if in.BuyType == enums.VideoBuyTypeFE && b.IsPurchased(ctx, uid, in.VideoId) {
		in.BuyType = enums.VideoBuyTypeME // 更改为购买余下全集
	}

	// 初始化
	item = &model.GenVideoSaleBillItem{
		Uid:           uid,
		ConsumedCoins: detail.CompletePanguCoin, // 默认全集购买，全集盘古币
		BuyType:       in.BuyType,
		AuthDetail:    *detail,
		BuyStatus:     enums.VideoBuyStatusYes,
	}

	// 获取视频剧集
	item.Episodes, err = b.GetEpisodesBuyList(ctx, item.Uid, *in)
	if err != nil {
		return nil, exception.Newf(`视频剧集不存在 %w`, err)
	}

	// 需要购买集数
	episodesNum := len(item.Episodes)
	if episodesNum == 0 {
		return nil, exception.New(`已购买视频全集`)
	}

	// 非全集购买
	if in.BuyType != enums.VideoBuyTypeFE {
		// 单集购买
		if in.BuyType == enums.VideoBuyTypeSE {
			item.BuyStatus = enums.VideoBuyStatusNot // 设置连续购买中
		} else {
			// 余集购买：是否连续剧，减除掉免费级数
			if detail.IsSeries == uint(enums.VideoSeriesMany) {
				episodesNum -= int(detail.FreePlayNum)
			}
		}
		// 减除免费级数，是否有剩余
		if episodesNum <= 0 {
			return nil, exception.New(`已购买视频全部剧集`)
		}
		// 计算购买盘古币
		item.ConsumedCoins = detail.SinglePanguCoin * uint(episodesNum)
	}
	return
}

// FmtBuyEpisodeContent 格式化购买剧集内容
func (b *sVideoBuy) FmtBuyEpisodeContent(episode []model.VideoBuyEpisodesItem) string {
	if len(episode) == 0 {
		return ``
	}
	return strings.Join(utils.NewArray(episode).Strings(dao.VideoBuyEpisodes.Columns().EpisodesName), `,`)
}
